﻿using OpenCvSharp;
using OpenCvSharp.Features2D;
using OpenCvSharp.Flann;
using OpenCvSharp.Internal.Vectors;
using OpenCvSharp.XFeatures2D;
using System;
using System.CodeDom;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Security.Cryptography.Xml;
using System.Text;
using System.Threading.Tasks;
using static System.Net.Mime.MediaTypeNames;

namespace OpenCV
{
    public static class OpenCVHelper
    {
        /// <summary>
        /// 缩放
        /// </summary>
        public static Mat Resize(Mat mat, int w,int y)
        {
            Mat result = new Mat();
            Cv2.Resize(mat, result, new OpenCvSharp.Size(w, y));
            return result;
        }
        /// <summary>
        /// 模糊-高斯函数
        /// </summary>
        public static Mat GaussianBlur(this Mat mat, int w=7, int h=7,double sx=1)
        {
            Mat result = new Mat();
            // 高斯核为7*7
            Cv2.GaussianBlur(mat, result, new OpenCvSharp.Size(w, h), sx);
            return result;
        }
        /// <summary>
        /// 图像置灰-用高斯函数设置模糊会比较好
        /// </summary>
        public static Mat ToColor(this Mat mat, ColorConversionCodes coloCode=ColorConversionCodes.BGR2GRAY)
        {
            Mat result = new Mat();
            mat = mat.GaussianBlur(5, 5);
            Cv2.CvtColor(mat, result, coloCode);
            return result;
        }
       
        
        public static Mat Test()
        {
            Mat result = new Mat();
            int val1 = 23;
            int val2 = 23;
            //Cv2.CreateTrackbar("Threshold1", "Parameters",ref val1, 255);
            //Cv2.CreateTrackbar("Threshold2", "Parameters",ref val2, 255);
            return result;
        }
        public static Mat ToDilate(this Mat mat,int w,int h,int iterations= 1)
        {
            Mat result = new Mat();
            //内核
            Mat kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new OpenCvSharp.Size(w, h));
            Cv2.Dilate(mat,result, kernel, iterations:iterations);//mat转bitmap
            return result;
        }
        /// <summary>
        /// 获取检测图像轮廓的函数
        /// </summary>
        /// <param name="mat">灰图</param>
        /// <param name="source">原图</param>
        /// <param name="minArea">最小面积</param>
        /// <returns></returns>

        public static Mat GetCountours(this Mat mat, Mat source, int minArea = 1000)
        {


            //Mat binary = mat.Threshold(0, 255, ThresholdTypes.BinaryInv | ThresholdTypes.Otsu);
            //边框也会包括
            //Mat binary = mat.Threshold(0, 255, ThresholdTypes.BinaryInv );

            OpenCvSharp.Point[][] contours = null;
            //从黑色背景里找白色轮廓
            Cv2.FindContours(mat, out contours, out HierarchyIndex[] hierarchyIndexes, mode: RetrievalModes.Tree,
                 method: ContourApproximationModes.ApproxNone);

            int contourIdx = -1; // 要绘制的轮廓索引
            Scalar contourColor = new Scalar(255, 0, 255); // 
            int thickness = 1; //7 轮廓线的粗细
            var lineType = LineTypes.Link8; // 线的类型
             // Draw contours
             //为三通道：颜色
             //Mat dst = mat.Clone().ToColor(ColorConversionCodes.GRAY2RGB);//
            Mat dst = source.Clone();//

            List<OpenCvSharp.Point[]> SureContours = new List<OpenCvSharp.Point[]>();

            foreach (var item in contours)
            {
                var ares = Cv2.ContourArea(item);
                if (ares > minArea)
                {
                    SureContours.Add(item);
                    //Cv2.DrawContours(dst, item, contourIdx, contourColor, thickness, lineType);
                    //拐点找到轮廓长度，弧长函数 估算是那种形状
                    double peri =Cv2.ArcLength(item, true);
                    //闭合轮廓-获得顶点
                    var approx =Cv2.ApproxPolyDP(item, 0.02* peri,true);
                    //边框
                    var rect=Cv2.BoundingRect(approx);
                    //用矩形显示
                    Cv2.Rectangle(dst, rect,  Scalar.Red, 2);
                    //输出
                    //Cv2.PutText(dst, "P:" + approx.Length, new OpenCvSharp.Point(rect.X + rect.Width + 20, rect.Y + 45), HersheyFonts.HersheyComplex, 1.7, Scalar.Green);
                    //输出
                    //Cv2.PutText(dst, "A:" + ares, new OpenCvSharp.Point(rect.X + rect.Width + 20, rect.Y + 90), HersheyFonts.HersheyComplex, 1.7, Scalar.Green);

                }
            }
            //Cv2.DrawContours(dst, contours, contourIdx, contourColor, thickness, lineType);
            Cv2.DrawContours(dst, SureContours, contourIdx, contourColor, thickness, lineType);
            //Cv2.DrawContours(mat, result, -1, new Scalar(255, 0, 255), 7,hierarchy: resultMat);
            return dst;

        }
        /*
        public static Bitmap ToBitmap(this Mat mat)
        {
          return   OpenCvSharp.Extensions.BitmapConverter.ToBitmap(mat);//mat转bitmap
        }
        /// <summary>
        /// 肯尼边缘检测器
        /// </summary>
        /// <param name="mat"></param>
        /// <param name="threshold1"></param>
        /// <param name="threshold2"></param>
        /// <returns></returns>
        public static Mat Canny(this Mat mat,double threshold1, double threshold2)
        {
            Mat result = new Mat();
             Cv2.Canny(mat, result, threshold1, threshold2);
            return result;
        }
        */


        public static Characteristic GetHarris(this Mat mat,float thrould)
        {
            Mat imgSrc=mat.Clone();
            //灰度化
            Mat gray = mat.ToColor();

            //ConnerHarris harris = new ConnerHarris();
             Mat des = new Mat(gray.Size(), MatType.CV_32FC1);
            //创建SURF对象-保存角点检测几个，其类型和尺寸与src相同
            int blockSize = 4;//邻域大小，详见参考文献3-4
            int ksize = 1;//Sobel算子的孔径大小，只能取1、3、5、7
            float k = 0.04f;// 权重系数，一般取0.04~0.06
            Cv2.CornerHarris(gray, des, blockSize, ksize, k);
            
             Mat imgNorm = new Mat();
          
            Cv2.Normalize(des, imgNorm, 0, 255, NormTypes.MinMax);
            List<KeyPoint> kp=new List<KeyPoint>();
            for (int i = 0; i < imgNorm.Rows; i++)
            {
                for (int j = 0; j < imgNorm.Cols; j++)
                {
                    if (imgNorm.At<float>(i, j) > thrould)
                    {
                        Cv2.Circle(imgSrc, j, i, 5, Scalar.Red);
                        KeyPoint kk = new KeyPoint(i,j,imgNorm.At<float>(i, j));
                        kp.Add(kk);
                                
                    }
                }
            }



            Characteristic cs = new Characteristic();
            cs.des = des;
            cs.kp = kp.ToArray();
            cs.result = imgSrc;
            return cs;
        }
        public static Characteristic GetSIFT(this Mat mat)
        {
            //灰度化
            Mat gray = mat.ToColor();

            //创建SURF对象
            var sift = SIFT.Create(500); // 创建一个SURF对象，阈值设为500
            var keypoints = new KeyPoint[10];
            var descriptors = new Mat();
            Mat mask = new Mat();
            //进行检测
            sift.DetectAndCompute(gray, mask, out keypoints, descriptors); // 在图像上检测关键点和计算描述符

            Mat result = new Mat();

            //绘制关键点
            Cv2.DrawKeypoints(mat, keypoints, result);

            Characteristic cs=new Characteristic();
            cs.des = descriptors;
            cs.kp = keypoints;
            cs.result = result;
            return cs;
        }

        /// <summary>
        /// 速度比sift快
        /// </summary>
        /// <param name="mat"></param>
        /// <returns></returns>
        public static Characteristic GetSURF(this Mat mat)
        {
            //灰度化
            Mat gray = mat.ToColor();
            //创建SURF对象
            var surf = SURF.Create(500); // 
            
            KeyPoint[] keypoints = null;
            var descriptors = new Mat();
            Mat mask = new Mat();
            //进行检测
            surf.DetectAndCompute(gray, mask, out keypoints, descriptors); // 在图像上检测关键点和计算描述符
            
            Mat result = new Mat();

            //绘制关键点
            Cv2.DrawKeypoints(mat, keypoints, result);
            Characteristic cs = new Characteristic();
            cs.des = descriptors;
            cs.kp = keypoints;
            cs.result = result;

            return cs;
        }
        /// <summary>
        /// 可以实时视频
        /// </summary>
        /// <param name="mat"></param>
        /// <returns></returns>
        public static Characteristic GetORB(this Mat mat)
        {
            //灰度化
            Mat gray = mat.ToColor();

            //创建SURF对象
            var orb = ORB.Create(); // 创建一个SURF对象，阈值设为500
            KeyPoint[] keypoints = null;
            var descriptors = new Mat();
            Mat mask = new Mat();
            //进行检测
            orb.DetectAndCompute(gray, mask, out keypoints, descriptors); // 在图像上检测关键点和计算描述符

            Mat result = new Mat();

            //绘制关键点
            Cv2.DrawKeypoints(mat, keypoints, result);
            Characteristic cs = new Characteristic();
            cs.des = descriptors;
            cs.kp = keypoints;
            cs.result = result;

            return cs;
        }

        /// <summary>
        /// 暴力匹配
        /// </summary>
        /// <param name="mat"></param>
        /// <returns></returns>
        public static Mat GetBFMatcher(Mat search,Mat orign)
        {
            //灰度化
            //var  img1 = search.GetSIFT();
            //var  img2 = orign.GetSIFT();
            //var img1 = search.GetSURF();
            //var img2 = orign.GetSURF();
            var img1 = search.GetORB();
            var img2 = orign.GetORB();
            img1.result.SaveImage("F:/D1.jpg");
            img2.result.SaveImage("F:/D2.jpg");
            //var bf=BFMatcher(img1.des, img2.des);
            var bf=new BFMatcher(NormTypes.L1);
            var match = bf.Match(img1.des, img2.des);
            Mat result = new Mat();

            Cv2.DrawMatches(img1.result, img1.kp, img2.result, img2.kp,match, result);
      

            //绘制关键点
          


            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="mat"></param>
        /// <returns></returns>
        public static Mat GetFlann(Mat search, Mat orign)
        {
            //灰度化
            //var img1 = search.GetSIFT();
            //var img2 = orign.GetSIFT();
            var img1 = search.GetORB();
            var img2 = orign.GetORB();
            //var bf=BFMatcher(img1.des, img2.des);

            var flann = new FlannBasedMatcher();

            var match = flann.KnnMatch(img1.des, img2.des,k:2);
            List<DMatch[]> Goods = new List<DMatch[]>();
            foreach (var item in match)
            {
                var m = item[0];
                var n = item[1];
                
                List<DMatch> Good = new List<DMatch>();
                if (m.Distance > 0.99 *n.Distance)
                {
                    Good.Add(m);
                }
                if (Good.Count() > 0)
                {
                    Goods.Add(Good.ToArray());
                }

                //foreach (var d in item)
                //{
                //    if (d.Distance > 0.7)
                //    {
                //        Good.Add(d);
                //    }
                //}
                
                
            }
            Mat result = new Mat();

            Cv2.DrawMatchesKnn(img1.result, img1.kp, img2.result, img2.kp, Goods, result);


            //Cv2.DrawMatches(img1.result, img1.kp, img2.result, img2.kp, match, result);


            //绘制关键点



            return result;
        }

        /// <summary>
        /// 暴力匹配
        /// </summary>
        /// <param name="mat"></param>
        /// <returns></returns>
        public static Mat GetFlann(Characteristic search, Characteristic orign,double rate=0.7)
        {
            //灰度化
            //var img1 = search.GetSIFT();
            //var img2 = orign.GetSIFT();
            var img1 = search;
            var img2 = orign;
            //var img1 = search.GetORB();
            //var img2 = orign.GetORB();
            //var bf=BFMatcher(img1.des, img2.des);

            var flann = new FlannBasedMatcher();

            var match = flann.KnnMatch(img1.des, img2.des, k: 2);

            // 使用RANSAC算法筛选误匹配
            var Matfundamental = Cv2.FindFundamentalMat(points1, points2, FundamentalMatMethods.Ransac, 1, 0.5);

            List<DMatch[]> Goods = new List<DMatch[]>();
            foreach (var item in match)
            {
                var m = item[0];
                var n = item[1];

                List<DMatch> Good = new List<DMatch>();
                if (m.Distance > rate * n.Distance)
                {
                    Good.Add(m);
                }
                else
                {

                }
                if (Good.Count() > 0)
                {
                    Goods.Add(Good.ToArray());
                }

                //foreach (var d in item)
                //{
                //    if (d.Distance > 0.7)
                //    {
                //        Good.Add(d);
                //    }
                //}


            }
            
            
            
            Mat result = new Mat();

            Cv2.DrawMatchesKnn(img1.result, img1.kp, img2.result, img2.kp, Goods, result);
            //RANSAC消除误匹配
            var strPts = KeyPointsToMat(img1.kp);
            var desPts = KeyPointsToMat(img1.kp);
            Cv2.FindHomography(strPts, desPts);
            //Cv2.DrawMatches(img1.result, img1.kp, img2.result, img2.kp, match, result);


            //绘制关键点



            return result;
        }
        public static Mat KeyPointsToMat(KeyPoint[] keypoints)
        {
            if (keypoints == null || keypoints.Length == 0)
                return null;

            // 创建一个空的Mat，用于存储特征点的坐标
            Mat mat = new Mat();

            for (int i = 0; i < keypoints.Length; i++)
            {
                mat.Set(i, 0, keypoints[i].Pt); // 将特征点的坐标存入Mat
            }

            return mat;
        }

        /// <summary>
        /// 暴力匹配
        /// </summary>
        /// <param name="mat"></param>
        /// <returns></returns>
        public static Mat GetBFMatcher(Characteristic search, Characteristic orign)
        {
            //灰度化
            //var  img1 = search.GetSIFT();
            //var  img2 = orign.GetSIFT();
            //var img1 = search.GetSURF();
            //var img2 = orign.GetSURF();
            //var img1 = search.GetORB();
            //var img2 = orign.GetORB();
            //search.result.SaveImage("F:/D1.jpg");
            //orign.result.SaveImage("F:/D2.jpg");
            //var bf=BFMatcher(img1.des, img2.des);
            var bf = new BFMatcher(NormTypes.L1);
            var match = bf.Match(search.des, orign.des);
            Mat result = new Mat();

            Cv2.DrawMatches(search.result, search.kp, orign.result, orign.kp, match, result);


            //绘制关键点



            return result;
        }

   
        /// <summary>
        /// 单映性矩阵
        /// </summary>
        /// <param name="mat"></param>
        /// <returns></returns>
        public static Mat juz(Mat search, Mat orign)
        {
            //灰度化
            var img1 = search.GetSURF();
            var img2 = orign.GetSURF();

            //var bf=BFMatcher(img1.des, img2.des);

            var flann = new FlannBasedMatcher();

            var match = flann.KnnMatch(img1.des, img2.des, k: 2);
            List<DMatch[]> Goods = new List<DMatch[]>();
            foreach (var item in match)
            {
                var m = item[0];
                var n = item[1];

                List<DMatch> Good = new List<DMatch>();
                if (m.Distance > 0.7 * n.Distance)
                {
                    Good.Add(m);
                }
                if (Good.Count() > 0)
                {
                    Goods.Add(Good.ToArray());
                }




            }
            Mat result = new Mat();
            Cv2.DrawMatchesKnn(img1.result, img1.kp, img2.result, img2.kp, Goods, result);

            //Cv2.DrawMatches(img1.result, img1.kp, img2.result, img2.kp, match, result);


            //绘制关键点



            return result;
        }


    }
}
